/*
 * rulebase.h
 *
 *  Created on: March 19, 2011
 *      Author: Mo
 */
#pragma once
#include "imagematrix.h"
#include "longinstance2.h"
#include "longinstance2b.h"
#include "cmconstants.h"
#include "eightlayerim.h"
//#include "rulebase2b.h" // we need this to define the RuleBase::RuleBase(RuleBase2B rb2b). This constructor is needed to calculate the feature removal order used in utilityFunc::fillRemoveOrder()
#include <vector>
#include <string>
using namespace std;

#ifndef RULE_SIZE_CALC_METHOD_
#define RULE_SIZE_CALC_METHOD_
enum ruleSizeCalcMethod
{
	onlyRules,
	onlyLI2s,	//tells that the RB is composed of LongInstance2 objects and only calculate their size, forget about coos.
	rulesAndCoos,
};
#endif

class RuleBase2B;
class FreeFormRuleBase;

/**
 * This class stores a set of rules (i.e. instances or LongInstance objects) as a rule base.
 * It keeps the track of most important features in the rule base. Whenever a new rule (LongInstance) is added,
 * it updates that array which sorts the features based on their importance.
 */
class RuleBase
{
private:
	vector<LongInstance2> instance;	//!< Set of instances stored in the rule base.
	int ruleSize;					//!< The linear size of each rule; i.e. number of features in an instance; i.e. the elements in the IF part.
	int conflictNum;				//!< Total number of conflicting instances in the whole rulebase.
	int repeatationNum;				//!< Total number of non-unique instances in the whole rule base.
	double separability;			//!< The separability measure of the rulebase.
	vector<int> mostGainArray;		//!< Sorts the features from the most gainful feature to the least gainful feature.
	bool separabilityIsValid;

	/**
	 * Calculates the information in the whole instances (based on the label). (ref. AI book P378).
	 * This takes into account the repeatation of the instances. An instant that has been repeated twice
	 * has the weight of two separate instances.
	 * \return The total information in the instance vector.
	 */
	double totalInformationWithRepeatation (void) const;


	/**
	 * Returns the feature with the most gain (i.e. bestFeatureArray[0])
	 * \return The index of the feature with the most gain.
	 * \note Call updateMostGainArray() before if the array might be not sorted
	 */
	int featureWithMostGain(void) const;

	/** 
	 * Returns the index of the instance in the rulebase that matches the given instance.
	 * Returns -1 if this rulebase does not include the given instance.
	 * \note It does not check the label of the rule, only features are checked.
	 */
	int match (const LongInstance2& sampleRule) const;

public:

	/**
	 * This copies the content of vector<int> mostGainArray into the array whose pointer is given.
	 * There is no need to worry for the size of the given array for now, as the size is always
	 * equal to the size of mostGainArray when it is called.
	 * However, for more security, I'd rather add the size to make it fool proof.
	 * \param out targetMGA the pointer to the array to which the mostGainArray should be copied
	 * \param mGASize the size of the given array.
	 */
	void copyMostGainArray(int* targetMGA, int mGASize);

	/** When creating the array of childRB in FixedTreeNode, because we create a static
	 * array of RBs, the default constructor is called for all childRBs. The internal members
	 * who depend on the dynamic ruleSize cannot be created then. This function should be 
	 * called to take care of this issue. It sets the ruleSize and mostGainArray[] for a RuleBase
	 * object which has been created using default constructor.
	 * \note I checked this, this is called from FixedTreeNode::makeArraysOfBlankNode(). That function
	 * by itself takes care of other dynamic arrays in FixedTreeNode.
	 */
	void copySizesFrom (const RuleBase& other);

	/**
	 * Adds a new LongInstance object to the calling RuleBase object and then updates the
	 * mostGainArray[].
	 * \param ins The instance to be added to the instance vector.
	 * \note ALWAYS call this function or addInstanceNOUpdateMGA() for adding an instance to the
	 * rule base. This will guarantee that the rule base is repeating and conflict free. This way
	 * we won't need to call either compress() or resolve().
	 */
	void addInstanceUpdateMGA (const LongInstance2& ins);

	/**
	 * Adds a new LongInstance object (AKA rule) to the calling RuleBase object and
	 * does not update any data member. Used for adding batches of LongInstances. The
	 * data members such as mostGainArray[] should be manually updated in the end
	 * when using this for adding batch of LongInstances.
	 * \note This function does not resolve conflicts; i.e. if a conflicting rule
	 * (LongInstance object) already exists in the rb, it adds the given LongInstance
	 * and updates the conflictNum of the conflicting rule.
	 *  \param ins The instance to be added to the instance vector.
	 */
	void addInstanceNOUpdateMGA(const LongInstance2& ins);

	/**
	 * Returns the LongInstance in the RuleBase object whose index is given.
	 * \param index The index of the LongInstance
	 * \return the instance whose index is given
	 */
	LongInstance2 getInstance (int index) const;

	/**
	 * Returns the label of the instance whose index is given.
	 * \param index The index of the instance whose label should be returned.
	 * \return The label of the instance whose index is given
	 */
	int getLabelOfIndex (int index) const;

	/**
	 * Updates and returns the separability of the rule base.
	 * This function does not work yet.
	 * \return The separability data member.
	 */
	double updateSeparability (void);

	/**
	 * For each pair of conflicting instances, deletes the one with lower occurrences.
	 */
	void resolve (void);

	/** This will replace resolve() for the new implementation of the class RuleBase.
	 * \note resolve2() can only be called for repeatation-free RuleBase objects. i.e.
	 * the objects in which no instance repeats with the same label. Because in the new
	 * implementation of addInstanceNOUpdateMGA(), no repeatation happens, it is safe to 
	 * call this function instead of resolve()*/
	void resolve2 (void);

	/** Same as resolve2() but uses iterators for indexing vectors. */
	void resolve3 (void);

	/**
	 * updates the bestFeatureArray, from highest gain feature to lowest gain feature.
	 */
	void updateMostGainArray (void);

	/**
	 * Prints the rulebase to the screen, writing only the feature array of each instance.
	 */
	void print(void) const;

	/**
	 * Prints the rulebase to the screen, writing the whole info on each instance.
	 */
	void printDetail(void) const;

	/**
	 * Returns the information gain if we choose the feature featureIndex to be the root of subtree (ref. AI book P379)
	 * \note The gain is calculated using all the instances, not only one.
	 * \param featureIndex Index of the feature
	 * \return Information gain of the feature with the given index.
	 */
	double gain(int featureIndex) const;

	/**
	 * Tells if all the instances in the object have the same label or not.
	 * \return True if all the instances belong to the same class, false otherwise.
	 */
	bool allSameClass (void) const;

	/**
	 * Returns the number of instances in this RuleBase.
	 */
	int getInstanceNum() const;

	/** Returns the number of features in each rule (not the number of neighborhoods). */
	int getInstanceSize(void) const;

	/** sets the ruleSize data member.*/
	void setInstanceSize (int size);

	/**
	 * Returns the feature value of given feature in the given instance
	 * \param inst The index of the instance
	 * \param feature The feature in the given instance
	 * \return The value of the specified feature.
	 */
	int getFeature(int inst, int feature) const;

	/**
	 * Returns the label of the given instance in the rule base object
	 * \param The index of instance whose label will be returned.
	 * \return The label of the given instance.
	 */
	int getLabel (int inst) const; //NOTE: THE SAME AS getLableOfIndex(). STUPID!


	/**
	 * Takes an image (in ImageMatrix format), applies this RuleBase to it and saves the result as another 
	 * ImageMatrix object.
	 * \param source the input image data
	 * \param target the ImageMatrix that will hold the new image data after applying the rulebase.
	 * \note This function works only for rulebases that have only one neighborhood for the instances
	 * (i.e. single step rulebases, not those representing a path from phase0 to phase1 to phase2 to...).
	 */
	void makeNextImagePhase (const ImageMatrix& source, ImageMatrix& target, int nbRadius = NRADIUS) const;

	/** Default constructor. Initializes everything to zero. */
	RuleBase();

	/** Constructor of an RB with a given rule size. */
	RuleBase(int ruleSize);

	/** Copy constructor. */
	RuleBase (const RuleBase& other);

	/**
	* Given two ImageMaterix objects (i.e. binary images), this function creates the
	* RuleBase corresponding to the transition from the old to the new ImageMatrix.
	* \param matrixOld The ImageMatrix representing the current state
	* \param matrixOld The ImageMatrix representing the next state
	* \param nbRadius The neighborhood radius for the LongInstances in the RuleBase object.
	*/
	RuleBase (ImageMatrix& matrixOld, ImageMatrix& matrixNew, int nbRadius = NRADIUS);

	/** destructor */
	~RuleBase ();

	/**
	 * Creates the rulebase according to multiple imagematrices. It tracks the history of 
	 * changes in the neighborhood by adding the neighborhood of each pixel over the time to 
	 * each longinstance in the rulebase and uses the last imagematrix for the labels.
	 * The neighborhood num of the RuleBase will then be imageNum - 1.
	 * The neighborhood radius will be nbRadius.
	 */
	RuleBase (const ImageMatrix* images, int imageNum, int nbRadius = NRADIUS);

	/**
	 * Creates the rulebase according to multiple imagematrices in the given EightLayerIM object. 
	 * It tracks the history of changes in the neighborhood by adding the neighborhood of each 
	 * pixel over the time to each longinstance in the rulebase and uses the last imagematrix for 
	 * the labels. The neighborhood num of the RuleBase will then be eim.validImages - 1.
	 * The neighborhood radius will be nbRadius.
	 */
	RuleBase (const EightLayerIM& eim, int nbRadius = NRADIUS);

	/** Similar to above, but creates a rulebase using only the imagematrices in eim from start to end. i.e. the image matrices 
	 * start, start+1, ... end-1 are used to form the IF part and the imagematrix whose index is 'end' will
	 * be used to build the THEN part of the rule. The neighborhood radius will be nbRadius.
	 */
	RuleBase (const EightLayerIM& eim, int start, int end, int nbRadius = NRADIUS);

	/** Reads the individual LongInstance2B objects from a file with a certain format. Refer to a sample file for more info.*/
	RuleBase (string filename);

	/**
	 * Returns the index of the LongInstance that conflicts the given parameter. Returns -1
	 * if no conflicting rule exists.
	 */
	int indexOfConflicting (const LongInstance2& myRule) const;

	void setDynamicSizes(int size); //!< R.E. copySizeFrom, but directly inputs the dynamic size of the non-initialized members.

	/**
	 * Updates conflictNum and repeatationNum data members.
	 * \noe resolve() should be called before calling this function.
	 */
	void updateDataMembers4Resolved (void);
	
	/**
	 * Calculates the information in the whole instances (based on the label). (ref. AI book P378).
	 * Does not consider the repeatation of the instances. An instant that has been repeated twice
	 * has the same weight as a non-repeating instance.
	 * \return The total information in the instance vector.
	 */
	double totalInformationNORepeatation (void) const;

	/** 
	 * Returns the number of conflicts in the rules, no matter how many instances the rules have.
	 * For example, nb1->0, nb1->1, nb2->0, nb2->1, nb3->0 returns 2.
	 */
	int totalConflicts (void) const;

	/**
	 * Returnes the net information size (in bits) stored in an object.
	 * The returned value is the minimum number of bits that efficiently can give us all the information
	 * in a RuleBase object.
	 */
	int getNetInfoSizeInBits(ruleSizeCalcMethod method = onlyLI2s) const;

	///**returns an array that has the index of gain of the features from low to high.
	// * \note: This function creates a new dynamic int array. the caller is responsible 
	// * for deleting this array afterwards.
	// */
	//int* getSortedGainArray() const;

	///** Copies the sorted indices of the feature gains in the given array.
	// * \example if gain(f1) = 0.7, gain(f2) = 0.9 and gain(f3) = 0.2, 
	// * then indices[0] = 2, indices[1] = 1, indices[2] = 3.
	// */
	//void getSortedGainArray (int* indices, int size) const;

	/**
	 * like copyMostGainArray(), but limits the number of features to size of one neighborhood. 
	 * It creates the gain array with the size equal to one neighborhood, and then assign the 
	 * value to each element by calculating the average gain of all the features in that position 
	 * in all the neighborhoods.
	 * \note it is used to propose an order for discarding the unimportant neighbors in an extended
	 * RuleBase2B object.
	 */
	void getMostGainIndicesArray_SuperposeNBs (int* mGA, int size) const;

	int getNBsperOneLongInstance() const;

	void updateConflictAndRepeteationNum();

	friend void ConvertRB2BtoRB (const RuleBase2B& rb2b, RuleBase& rb);
	//friend void ConvertFFRBtoRB (const FreeFormRuleBase& ffrb, RuleBase& rb);

};
